package com.piece.file.preview;

import com.piece.core.framework.util.cache.CacheFactory;
import com.piece.core.web.util.ServletUtil;
import com.piece.core.framework.util.file.FileUtil;
import com.piece.file.enums.FilePreviewType;
import com.piece.file.model.TbSysFile;
import com.piece.file.service.TbSysFileService;
import com.piece.file.support.FileSource;
import com.piece.file.client.FileClientFactory;
import com.piece.file.support.FileAttribute;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class FilePreviewFactory {

    public static String FILE_CALLBACK_KEY = "only_office_callback_key_";

    @Autowired
    ApplicationContext context;

    @Value("${file.converted.charset:utf-8}")
    public String charset;

    @Value("${file.converted.simText:#{null}}")
    public String[] simText;

    @Value("${file.converted.media:#{null}}")
    public String[] media;

    @Value("${spring.resources.context:#{'/'}}")
    protected String contextPath;

    @Resource
    protected FileClientFactory fileClientFactory;

    @Resource
    protected TbSysFileService tbSysFileService;

    @Resource
    private CacheFactory cacheFactory;

    public Object[] getByUrl(String url) {
        Map<String, FilePreviewHandle> filePreviewHandleMap = context.getBeansOfType(FilePreviewHandle.class);
        FilePreviewType type = null;
        FileAttribute fileAttribute = null;
        try {
            fileAttribute = getFileAttribute(url);
            type = fileAttribute.getType();
            convertFileAttribute(fileAttribute, true);
        } catch (Exception e) {
            log.error("获取Office文件数据异常：{}", e.getMessage());
        }
        FilePreviewHandle filePreviewHandle = filePreviewHandleMap.get(type.getInstanceName());
        return new Object[]{ filePreviewHandle, fileAttribute};
    }

    public Object[] getByFileId(String fileId) {
        Map<String, FilePreviewHandle> filePreviewHandleMap = context.getBeansOfType(FilePreviewHandle.class);
        TbSysFile file = tbSysFileService.findById(fileId);
        FilePreviewType type = null;
        FileAttribute fileAttribute = null;
        try {
            String fileName = file.getOriginalFilename();
            String suffix = FileUtil.getFileSuffix(fileName);
            type = typeFromName(fileName);
            String url = ServletUtil.getRequestPrefix(contextPath) +  "/public/file/download/" + fileId;
            fileAttribute = FileAttribute.builder().type(type).suffix(suffix).name(fileName).url(url).path(fileId).build();
            convertFileAttribute(fileAttribute, true);
        } catch (Exception e) {
            log.error("获取Office文件数据异常：{}", e.getMessage());
        }

        FilePreviewHandle filePreviewHandle = filePreviewHandleMap.get(type.getInstanceName());
        return new Object[]{ filePreviewHandle, fileAttribute};
    }

    public Object[] getByFilePath(String filePath) {
        Map<String, FilePreviewHandle> filePreviewHandleMap = context.getBeansOfType(FilePreviewHandle.class);
        FilePreviewType type = null;
        FileAttribute fileAttribute = null;
        try {
            filePath = URLDecoder.decode(filePath, charset);
            String fileName = filePath.substring(filePath.lastIndexOf(File.separator) + 1);
            String suffix = FileUtil.getFileSuffix(fileName);
            type = typeFromName(fileName);
            String url = ServletUtil.getRequestPrefix(contextPath) +  "public/file/preview?path=" + URLEncoder.encode(filePath, charset);
            fileAttribute = FileAttribute.builder().type(type).suffix(suffix).name(fileName).url(url).path(filePath).build();
            convertFileAttribute(fileAttribute, true);
        } catch (Exception e) {
            log.error("获取Office文件数据异常：{}", e.getMessage());
        }

        FilePreviewHandle filePreviewHandle = filePreviewHandleMap.get(type.getInstanceName());
        return new Object[]{ filePreviewHandle, fileAttribute};
    }

    private FileAttribute getFileAttribute(String url) {
        // 路径转码
        String fileName = FileUtil.getFileNameFromUrl(url);
        if ("download".equals(fileName)) {
            fileName = url.substring(url.lastIndexOf(File.separator) + 1);
        }
        String suffix = FileUtil.getFileSuffix(fileName);
        FilePreviewType type = typeFromName(suffix);
        // 抽取文件并返回文件列表
        return FileAttribute.builder().type(type).suffix(suffix).name(fileName).url(url).build();
    }

    private void convertFileAttribute(FileAttribute fileAttribute, boolean isNative) throws Exception {
        String jsonBody = null;
        FilePreviewType type = fileAttribute.getType();
        String url = fileAttribute.getUrl();
        String decodedUrl = null != url ? URLDecoder.decode(url, charset) : "";
        if (isNative) {
            fileAttribute.setSource(FileSource.path);
        } else if (null != fileAttribute.getUrl()) {
            fileAttribute.setSource(FileSource.url);
        } else {
            fileAttribute.setSource(FileSource.file);
        }

        String key = FileUtil.md5(fileAttribute.getName().getBytes());
        fileAttribute.setKey(key);
        cacheFactory.get().setExpire(FILE_CALLBACK_KEY + key, fileAttribute, 60, TimeUnit.MINUTES);
    }

    /**
     * 查看文件类型
     */
    private FilePreviewType typeFromName(String name) {
        if (FileUtil.checkImageType("." + name)) {
            return FilePreviewType.picture;
        }
        if (FileUtil.checkArchiveType("." + name)) {
            return FilePreviewType.compress;
        }
        if (FileUtil.checkPdfType("." + name)) {
            return FilePreviewType.pdf;
        }
        if (FileUtil.checkOfficeType("." + name)) {
            return FilePreviewType.office;
        }
        if (FileUtil.checkTxtType("." + name)) {
            return FilePreviewType.simText;
        }
        if (FileUtil.checkVideoType("." + name)) {
            return FilePreviewType.media;
        }

        return FilePreviewType.other;
    }
}
